home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
IRIX 6.2 Applications 1996 May
/
SGI IRIX 6.2 Applications 1996 May.iso
/
dist
/
impr_dev.idb
/
usr
/
impressario
/
src
/
drivers
/
laserjetPJL
/
laserjetPJL.c.z
/
laserjetPJL.c
Wrap
C/C++ Source or Header
|
1996-05-06
|
40KB
|
1,192 lines
/**************************************************************************
*
* Copyright (c) 1992 Silicon Graphics, Inc.
* All Rights Reserved
*
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SGI
*
* The copyright notice above does not evidence any actual of intended
* publication of such source code, and is an unpublished work by Silicon
* Graphics, Inc. This material contains CONFIDENTIAL INFORMATION that is
* the property of Silicon Graphics, Inc. Any use, duplication or
* disclosure not specifically authorized by Silicon Graphics is strictly
* prohibited.
*
* RESTRICTED RIGHTS LEGEND:
*
* Use, duplication or disclosure by the Government is subject to
* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in
* Technical Data and Computer Software clause at DFARS 52.227-7013,
* and/or in similar or successor clauses in the FAR, DOD or NASA FAR
* Supplement. Unpublished - rights reserved under the Copyright Laws of
* the United States. Contractor is SILICON GRAPHICS, INC., 2011 N.
* Shoreline Blvd., Mountain View, CA 94039-7311
**************************************************************************
*
* File: laserjetPJL.c
*
* Summary:
* Print filter for HP LaserJet printers that support PJL.
*
* Description:
* Build PJL based on command line options and pre-pends that to
* data sent to printer. Reads stdin and expects a stiff file that
* is the image to print (can also read a PCL file as input). stiff
* data converted to PCL data.
*
* All output is sent to standard out.
*
* See Also:
* Developers will want to be familiar with the SGI filter/driver spec
* to understand exactly which options their drivers MUST implement,
* how options are to be parsed, and which option letters are reserved.
*
* Developers may be well advised to read the following man pages
* for more information on the libraries and functions used in
* this program:
*
* libpod -- general information on POD database library
* plp -- information on the builtin parallel port interface
*
* Specific libpod function calls are also documented, see their man pages.
*
**************************************************************************/
#ident "$Revision: 1.5 $"
#include "laserjetPJL.h"
#include "pclpjl.h"
#include "spool.h"
#include "impr_versions.h"
/* Some global variables, mostly set by command line options */
/* This is size of buffer read and processed (rather then read in entire
* image it is read in BUFDEFSIZE chunks).
*/
#define BUFDEFSIZE (1024*1024)
static int InBufSize = BUFDEFSIZE; /* size of buffer to allocate */
static char *InBuf = NULL; /* start of input data buffer */
static int PrinterModel; /* Printer model (4Mv, 5Si, etc.) */
static boolean StatusOnly = FALSE; /* Exit after reporting status? */
static boolean PrintWarnings = TRUE; /* Report warnings in status? */
static boolean SendPJL = TRUE; /* Send PJL commands ? */
static char DEBUG; /* What level debuging is turned on: */
/* 0...3==none, COARSE-, MEDIUM-, FINEDETAIL */
static int NumCopies = 1; /* Total total number of copies */
static int HorizRes; /* Printer horizontal resolution. */
static int VertRes; /* Printer vertical resolution. */
static int UserPageSize = LJ_DEFAULT_PAGE_SIZE;
static int Duplex = LJ_DEFAULT_DUPLEX; /* Duplex option */
static int Tray = LJ_DEFAULT_TRAY; /* Tray to use */
static int OutBin = LJ_DEFAULT_OUTBIN; /* Output tray to use */
static int Darkness = LJ_DEFAULT_DARKNESS; /* Darkness settings */
static boolean FullBleed = FALSE; /* Doing full-bleed B size */
static int pclFile = FALSE; /* True if input is already pcl */
static char *PrinterName = NULL; /* Printer name is required */
static FILE *infile = stdin; /* input from stdin or file */
/* Image file header information */
static PSTImageHeader ImageHeader;
static STStream *ImageStream = NULL;
/* The program name and version, for debug messages */
static char *ProgramName = NULL;
/* Printer POD information database structures */
static PDInfoStruct *Pod; /* printer object database struct*/
static time_t PodModTime; /* time of last POD modification */
/* An array of error messages, set later */
static PDMessageStruct ErrMsgs[PD_MESSAGE_MAX];
/***********************************************************************/
void set_program_name(const char* name) {
/* ASSUMES: nothing */
/* MODIFIES: global var ProgramName */
/* EFFECTS: Sets global name of program (ProgramName). */
/* Strips off any included path and leaves real name. */
if ((ProgramName = strrchr(name,'/')) != NULL) ProgramName++;
else ProgramName = (char*)name;
}
/***********************************************************************/
void hold_signals(void) {
/* REQUIRES: nothing
* MODIFIES: signal handling environment
* EFFECTS: puts key signals on hold until release_signals is called.
*
* This function HOLDS the most common catchable interrupts.
* This is important in the UNIX environment because many
* software interrupts (signals) are possible.
*/
(void)sighold(SIGINT);
(void)sighold(SIGTERM);
(void)sighold(SIGHUP);
(void)sighold(SIGQUIT);
}
/***********************************************************************/
void release_signals(void) {
/* REQUIRES: nothing
* MODIFIES: signal handling environment
* EFFECTS: releases key signals from any hold condition
*
* This function RELEASES the most common catchable interrupts.
* This is important in the UNIX environment because many
* software interrupts (signals) are possible.
*/
(void)sigrelse(SIGINT);
(void)sigrelse(SIGTERM);
(void)sigrelse(SIGHUP);
(void)sigrelse(SIGQUIT);
}
/***********************************************************************/
void print_usage_message(void)
{
/* ASSUMES: nothing */
/* MODIFIES: stderr */
/* EFFECTS: prints usage message */
/* "/usr/lib/print/laserjetPJL- h" will print usage */
fprintf(stderr, "\nUSAGE:\n%s -P PrinterName [options] [filename]\n\n",
ProgramName);
fprintf(stderr,
"\tWhere [options] is one or more of:\n\n"
"\t[-s] Update POD status files only, don't print.\n"
"\t[-X int] Horizontal Resolution, in dpi (300 or 600).\n"
"\t[-Y int] Vertical Resolution, in dpi (300 or 600).\n"
"\t[-S string] Paper Size (e.g., A, A4, Legal).\n"
"\t[-n int] Number of copies.\n"
"\t[-d string] Set Duplex option.\n"
"\t (string= \"off\",\"bindShortSide\",\"bindLongSide\").\n"
"\t[-j string] Set print darkness.\n"
"\t (string= \"Normal\",\"Light\",\"Dark\",\"Economode\").\n"
"\t[-T string] Set media tray (string= \"upper\",\"lower\",\"either\",\"manual\").\n"
"\t[-R string] Ouput tray (string= \"top\",\"rear\").\n"
"\t[-D] Debug mode, appends to logfile.\n"
"\t[-B int] Internal buffer size in bytes.\n"
"\t[-h] Print usage message.\n"
"\t[-J] Do not send PJL commands. Use for printers that \n"
"\t do not support PJL. Some options may not work if\n"
"\t this is used. (PJL used to set resolution, paper size, \n"
"\t number of copies, duplex option, and paper tray to use.)\n"
"\t[-F string] Format of file to print. String can be 'pcl' or 'stiff'\n"
"\t If 'pcl' driver processes command line args and puts a.\n"
"\t PJL wrapper arouind the file and sends as is. stiif is\n"
"\t the default.\n"
"\n"
"\t[filename] A file in appropriate image format.\n"
"\n");
}
/***********************************************************************/
void process_header_options(char* options)
/* ASSUMES: options is a null-terminated string containing
* options in a command-line format
* EFFECTS: turns options into an argc and argv and calls
* process_args on the resulting argument list.
* Informs process_args that these are image header options,
* so that process_args can apply proper precedence.
*/
{
int argc = 0;
char *argptr;
char *argv[MAXNUMARGS];
argv[argc++] = ProgramName; /* set the initial argument */
argptr = strtok(options, DELIMITER_STRING); /* get first argument */
while (argptr) {
argv[argc++] = argptr; /* save argument string ptr & incr count */
argptr = strtok(NULL, DELIMITER_STRING); /* get next argument */
}
optind = 1; /* Reset array pointer so that getopts starts over */
process_args(argc, argv);
}
/***********************************************************************/
boolean process_args(int argc, char* argv[]) {
/* ASSUMES: argc, argv in valid format */
/* MODIFIES: has side effects on global state, like debug, files, */
/* etc.. */
/* EFFECTS: processes all the command line args using getopts. */
/* Sets a bunch of state depending on which args are passed. */
int c, ii, errflag = 0;
opterr = 0; /* Turn off getopt debug messages, we'll print our own */
while ((c = getopt(argc, argv, "P:F:JhstX:Y:S:n:d:j:T:R:DB:L:")) != -1) {
switch (c) {
case 'P':
PrinterName = strdup(optarg);
break;
case 'J':
SendPJL = FALSE;
pjlsetNoPJL(TRUE);
break;
case 'h':
print_usage_message();
exit(0);
case 's':
StatusOnly = TRUE;
break;
case 'X':
HorizRes = atoi(optarg);
break;
case 'Y':
VertRes = atoi(optarg);
break;
case 'S':
if (!strcasecmp(optarg,"BB+")) { /* Full bleed B size for 4V */
UserPageSize = PD_SIZE_B;
FullBleed = TRUE;
} else if ((UserPageSize = PDGetSizeCodeByName(optarg)) < 0) {
if (PDerrno != 0) {
PDPerror(ProgramName);
} else {
fprintf(stderr, "%s: Invalid page size '%s' selected.\n",
ProgramName, optarg);
}
fprintf(stderr, "%s: Using default page size '%s'.\n",
ProgramName, PDGetNameBySizeCode(LJ_DEFAULT_PAGE_SIZE));
UserPageSize = LJ_DEFAULT_PAGE_SIZE;
}
break;
case 'n':
NumCopies = atoi(optarg);
break;
case 'd': /* Duplex option */
Duplex = LJ_DEFAULT_DUPLEX;
for (ii=0; ii<LJ_NUM_DUPLEX_NAMES; ii++) {
if (!strcasecmp(LJ_DUPLEX_NAMES[ii],optarg)) {
Duplex = ii;
break;
}
}
break;
case 'j': /* Darkness option */
Darkness = LJ_DEFAULT_DARKNESS;
for (ii=0; ii<LJ_NUM_DARKNESS_NAMES; ii++) {
if (!strcasecmp(LJ_DARKNESS_NAMES[ii],optarg)) {
Darkness = ii;
break;
}
}
break;
case 'T': /* Media Tray */
Tray = LJ_DEFAULT_TRAY;
for (ii=0; ii<LJ_NUM_TRAY_NAMES; ii++) {
if (!strcasecmp(LJ_TRAY_NAMES[ii],optarg)) {
Tray = ii;
break;
}
}
break;
case 'R': /* Output Tray */
OutBin = LJ_DEFAULT_OUTBIN;
for (ii=0; ii<LJ_NUM_OUTBIN_NAMES; ii++) {
if (!strcasecmp(LJ_OUTBIN_NAMES[ii],optarg)) {
OutBin = ii;
break;
}
}
break;
case 'F': /* File format */
if (!strcasecmp("pcl",optarg)) pclFile = TRUE;
break;
case 'B': /* How big a buffer to allocate for data */
if (atoi(optarg) <= 0) {
fprintf(stderr, "%s: -%c (buffer size) option must have "
"a positive argument.\n", ProgramName, (char)c);
} else {
InBufSize = atoi(optarg);
}
break;
case 'D':
DEBUG++; /* Multiple -D flags will increment this. */
/* Coarse, medium, and fine detail are invoked by */
/* one, two, and three or more -D flags respectively. */
switch (DEBUG) {
case 0:
break;
case COARSEDETAIL:
pjlSetDebug(TRUE);
fprintf(stderr,"%s: Coarse detail debug messages enabled.\n",
ProgramName);
break;
case MEDIUMDETAIL:
fprintf(stderr,"%s: Medium detail debug messages enabled.\n",
ProgramName);
break;
case FINEDETAIL:
default:
fprintf(stderr, "%s: Fine detail debug messages enabled.\n",
ProgramName);
break;
}
break;
case '?':
/* Do NOT exit on bad options, just ignore them. */
fprintf(stderr, "%s: Ignoring an incomplete or "
"unrecognized option '-%c'.\n", ProgramName, (char)optopt);
errflag++;
break;
}
}
if (errflag) print_usage_message();
/* Now check all the arguments. If wrong we sometimes will just fix
* them and continue
*/
/* Was a printer name specified? If not, exit */
if (PrinterName == NULL) {
fprintf(stderr, "%s: Printer name argument required:\n", ProgramName);
fprintf(stderr, "%s: \tYou must use the -P option to specify the "
"printer name.\n", ProgramName);
print_usage_message();
return FALSE;
}
/* Clamp reolution to 300 or 600 */
if (HorizRes != VertRes) {
HorizRes = DEFAULT_RESOLUTION;
VertRes = DEFAULT_RESOLUTION;
}
if ((HorizRes != 300) && (HorizRes != 600)) {
HorizRes = DEFAULT_RESOLUTION;
}
if ((VertRes != 300) && (VertRes != 600)) {
VertRes = DEFAULT_RESOLUTION;
}
/* Keep within reason (PJL typically allows 999 as max) */
if (NumCopies < 1) NumCopies = 1;
if (NumCopies > 999) NumCopies = 999;
/* Was a filename specified after the last argument?
* If so, grab it and try to open it instead of stdin.
*/
if (optind < argc) {
if (freopen(argv[argc-1], "r", stdin) == NULL) {
fprintf(stderr,"%s:\tUnable to open input file %s for reading.\n",
ProgramName, argv[argc-1]);
return FALSE;
} else {
if (DEBUG) {
fprintf(stderr, "%s: Reading from file '%s'.\n",
ProgramName, argv[argc-1]);
}
}
}
return TRUE;
}
/***********************************************************************/
int initialize_printer(FILE *out) {
/* Send the PJL commands for options and then go into PCL mode */
if (pjlEnterPJLMode(out) != LJ_EXIT_OK) return(FALSE);
if (pjlSetRes(out,HorizRes,VertRes) != LJ_EXIT_OK) return(FALSE);
if (pjlSetPageProtect(out,FALSE) != LJ_EXIT_OK) return(FALSE);
if (FullBleed) {
if (pjlSetPaperSize(out,-999) != LJ_EXIT_OK) return(FALSE);
} else {
if (pjlSetPaperSize(out,UserPageSize) != LJ_EXIT_OK) return(FALSE);
}
if (pjlSetCopies(out,NumCopies) != LJ_EXIT_OK) return(FALSE);
if (pjlSetDuplex(out,Duplex) != LJ_EXIT_OK) return(FALSE);
if (pjlSetTray(out,Tray) != LJ_EXIT_OK) return(FALSE);
if (pjlSetOutBin(out,OutBin) != LJ_EXIT_OK) return(FALSE);
if (pjlSetDarkness(out,Darkness) != LJ_EXIT_OK) return(FALSE);
/* Now go into PCL mode */
if (pjlEnterPCLMode(out) != LJ_EXIT_OK) return(FALSE);
if (pclResetPrinter(out) != LJ_EXIT_OK) return(FALSE);
/* Set resolution here because need to do it in pcl for raster garphics */
if (pclSetRes(out,HorizRes,VertRes) != LJ_EXIT_OK) return(FALSE);
/* Some models only allow tray via pcl */
if (pclSetTray(out,Tray) != LJ_EXIT_OK) return(FALSE);
return(TRUE);
}
/***********************************************************************/
void cleanup_and_quit(const int exitcode) {
/* ASSUMES: Program should exit upon handling this error.
* MODIFIES: out
* EFFECTS: Cleans up printer and exits.
* Used to handle interrupts
*/
/* Called on normal, error and interrupt exit */
if (DEBUG) {
fprintf(stderr,"%s: cleanup_and_quit called.\n",ProgramName);
}
(void) pclCleanupPrinter(stdout);
if (exitcode != NO_ERROR) {
/* Drain any remaining input */
if (InBuf) {
while (fgets(InBuf,InBufSize,infile))
;
}
}
if (ImageStream) (void) STClose(ImageStream);
exit(exitcode);
}
/***********************************************************************/
void setup_signal_handler(void) {
/* REQUIRES: nothing
* MODIFIES: signal handling environment
* EFFECTS:
*
* Sets up signal handler to catch the killing signals
* so that proper cleanup can be done
*
* This is important in the UNIX environment because many
* software interrupts are possible. We catch the most common
* catchable interrupts.
*
* Cleanup_and_quit is a void(int ...) function which
* should clean up the printer and then exit the program.
*/
(void) signal(SIGINT, cleanup_and_quit);
(void) signal(SIGTERM, cleanup_and_quit);
(void) signal(SIGHUP, cleanup_and_quit);
(void) signal(SIGQUIT, cleanup_and_quit);
}
/***********************************************************************/
boolean read_image_header(STStream *datafile, PSTImageHeader *imghdr)
{
/* ASSUMES: datafile open for reading, current position is image header
* EFFECTS: Reads and parses image file header,
* leaving datafile pointer at begin of binary data.
* Interprets any OPTIONS line, setting appropriate state.
* Returns true if successful, else false.
* MODIFIES: values in imghdr
* ALSO: Options line may alter any cmd-line settable variables.
* RETURNS: TRUE if successful read and valid data format,
* FALSE if failed read or invalid data format.
*/
if ( PSTReadImageHeader(datafile, imghdr) < 0 ) {
if (DEBUG > COARSEDETAIL) {
fprintf(stderr, "%s: Couldn't read image header:\n", ProgramName);
STPerror(ProgramName);
}
return FALSE;
}
/* Read the image header for the print driver options (command
* line options can be passed as a TAG in a STIFF file).
*/
process_header_options(imghdr->driverOptions);
return TRUE;
}
/***********************************************************************/
void print_image_info(PSTImageHeader *imghdr) {
/* REQUIRES: imghdr is valid
* MODIFIES: stderr
* EFFECTS: writes image information to stderr
*/
/* Called if in debug mode */
int img_type;
switch (imghdr->type) {
case ST_TYPE_K:
case ST_TYPE_W:
case ST_TYPE_RGB:
case ST_TYPE_CMAP:
case ST_TYPE_MASK:
case ST_TYPE_SEP: /* synonym for TYPE_CMY and TYPE_CMYK */
img_type = imghdr->type;
break;
case ST_TYPE_YMC:
img_type = N_IMAGE_TYPES - 3;
break;
case ST_TYPE_YMCK:
img_type = N_IMAGE_TYPES - 2;
break;
default:
img_type = N_IMAGE_TYPES - 1;
break;
}
fprintf(stderr, "%s: Input Image Header Information:\n", ProgramName);
fprintf(stderr, "\t xsize\t ysize\t zsize\t bpp\t type\t format\tbytes\n");
fprintf(stderr, "\t %lu\t %lu\t %d\t %d\t %s\t %s\t %lu\n",
imghdr->width, imghdr->height, (int)imghdr->samplesPerPixel,
(int)imghdr->bitsPerSample,
IMAGE_TYPES[ img_type ],
IMAGE_FORMATS[ imghdr->plane ],
imghdr->imgbytes
);
}
/***********************************************************************/
/* ARGSUSED */
boolean read_image_data(STStream* datafile, int imgbytes, unchar* pagebuf)
{
/* ASSUMES: image header has been successfully read,
* datafile pointer is now pointing at first byte of data.
* Pagebuf points to an area of memory of size pagebufsize.
* MODIFIES: datafile, possibly pagebuf and pagebufsize
* EFFECTS: If pagebuf is NULL,
* allocates page buffer of appropriate size,
* sets pagebuf to point to it, and pagebufsize to indicate size.
* If pagebufsize is too small, reallocates pagebuf to fit.
* Finally, reads one image into memory starting at pagebuf.
* RETURNS: TRUE if successful, FALSE if failed.
*/
long numbytesread = 0;
/* Read the image data. */
if ((numbytesread = STRead(datafile, (void*)pagebuf, imgbytes)) < 0) {
fprintf(stderr, "%s: ERROR: Failure while reading image data.\n",
ProgramName);
STPerror(ProgramName);
return FALSE;
}
if (DEBUG) {
fprintf(stderr, "%s: Read %8ld bytes from input file.\n",
ProgramName, numbytesread);
}
/* Did we get what we expected? */
if (numbytesread < imgbytes) {
fprintf(stderr, "%s: ERROR: Data file length (%ld) shorter than "
"header indicates (%lu).\n", ProgramName, numbytesread, imgbytes);
return FALSE;
}
return TRUE;
}
/***********************************************************************/
boolean is_valid_format(PSTImageHeader *imghdr ) {
/* ASSUMES: image header has already been read
* MODIFIES: stderr (if debug mode set)
* EFFECTS: returns TRUE if image is of proper format, else FALSE.
* NOTES: Obviously, whether an image is valid is printer-specific.
*/
return (imghdr->plane == ST_PLANE_PACKED
&& imghdr->samplesPerPixel == 1
&& imghdr->type == ST_TYPE_K
&& imghdr->bitsPerSample == 1
&& imghdr->width > 0
&& imghdr->height > 0
);
}
/***********************************************************************/
boolean send_image(FILE *out, PSTImageHeader *imghdr, unchar *imagebuffer)
{
/* ASSUMES: image header has been successfully read,
* file pointer is now pointing at first byte of data
* MODIFIES: out
* RETURNS: if successful, returns true, else false.
* EFFECTS: Snags a row at a time out of the image,
*/
unchar* rowptr; /* ptr to current location in page buffer */
unsigned long curline = 0;
unsigned long rowlength_bytes;
unsigned long maxrowsperread, nrowsread, rowstoread;
/* Initialize graphics for this page */
if (pclPrepForGraphics(out) != LJ_EXIT_OK) {
fprintf(stderr,"%s: ERROR: Failed while initializing printer.\n",
ProgramName);
cleanup_and_quit(BAD_TRANSMISSION);
}
/* Number of bytes in one row of data */
rowlength_bytes = 1 + (imghdr->width - 1) / 8;
/* Our buffer is probably not big enough to read whole image, calculate
* what we can read.
*/
maxrowsperread = InBufSize / rowlength_bytes;
rowstoread = ImageHeader.height;
nrowsread = 0;
/* Hold signals while we send one buffer full of data */
hold_signals();
/* Send the data */
for (curline = 0; curline < ImageHeader.height; curline++) {
if (nrowsread == 0) {
nrowsread = rowstoread%maxrowsperread; /* do the odd piece first */
/* nrowsread is always 0 after first time through (because of
* the MOD command done above). Read MAX buffer then.
*/
if (nrowsread == 0) nrowsread = maxrowsperread;
if (DEBUG) fprintf(stderr, "%s: Reading image\n",ProgramName);
if (!read_image_data(ImageStream, rowlength_bytes*nrowsread,
imagebuffer)) {
cleanup_and_quit(BAD_DATA_FILE);
}
/* Buffer holds more then one row so set up a ptr that can
* be incremented to point to start of each row.
*/
rowptr = imagebuffer;
rowstoread -= nrowsread;
}
if (pclSendRow(out, rowptr, rowlength_bytes) != LJ_EXIT_OK) {
fprintf(stderr,"%s: ERROR while sending image data to "
"printer.\n",ProgramName);
cleanup_and_quit(BAD_DATA_FORMAT);
}
rowptr += rowlength_bytes; /* Point to next row */
nrowsread--; /* =0 when done with all rows in buffer */
}
(void) fflush(out);
if (pclPrepForNextPage(out) != LJ_EXIT_OK) {
fprintf(stderr,"%s: ERROR: Failed while initializing printer.\n",
ProgramName);
cleanup_and_quit(BAD_TRANSMISSION);
}
/* Release all the signals we were holding. */
release_signals();
return(TRUE);
}
/***********************************************************************/
void add_error_message(const int error_code, const char* error_string)
{
/*
* REQUIRES:
* If error_string is DEFAULT_MSG,
* error_code must be known to libpod.
* If error_string is not DEFAULT_MSG,
* error_string overrides default.
*
* EFFECTS:
* Appends one error to the global ErrMsgs array
* and increments global variable pod->error_count by one.
*
* This function adds an error code and corresponding message
* to the active error status. A message that matches the
* error code is added to the structure along with the error
* code.
*
* If the number of messages would be increased over PD_MESSAGE_MAX,
* then no message is added to the error_status struct.
*
* If an error code is needed which does not appear in the database,
* calling this function with non-null error_string allows the caller
* complete control over the contents of the string area.
* HOWEVER, this is NOT RECOMMENDED, as error codes and strings should
* follow those in the libpod library for future compatibility.
*
* DO NOT UNDER ANY CIRCUMSTANCES use codes which aren't POD-compliant
* Make sure any codes chosen fit in the existing hierarchy of codes.
*
* ASSUMPTIONS:
* By convention, pod->active_status->error_count
* is reset to 0 before each new set of error messages is added.
*
* Assumes the following global variables:
* static PDInfoStruct *pod;
* static PDMessageStruct ErrMsgs[PD_MESSAGE_MAX];
*
*/
if (Pod->active_status->error_count >= PD_MESSAGE_MAX) {
fprintf(stderr,
"%s: WARNING: POD message not added to database, has %d already.\n",
ProgramName, PD_MESSAGE_MAX);
return;
}
if (error_string == DEFAULT_MSG) {
/* Caller has chosen recommended path: Use default message. */
PDMakeMessage(&ErrMsgs[Pod->active_status->error_count], error_code);
} else {
/* Caller has asked to use custom info. Not recommended! */
/* First set the error code, then copy the string (carefully!) */
ErrMsgs[Pod->active_status->error_count].message_code = error_code;
(void) strncpy(ErrMsgs[Pod->active_status->error_count].message_text,
error_string, PD_STR_MAX - 1 );
ErrMsgs[Pod->active_status->error_count].message_text[PD_STR_MAX-1]
= '\0';
ErrMsgs[Pod->active_status->error_count].message_code = error_code;
}
/* NOTE: *DO NOT INCREMENT ERROR_COUNT IN THE CODE ABOVE*
* unless you absolutely MUST add multiple messages at once,
* because pod->error_count will be incremented by one on exit.
*
* The proper way to add multiple messages is to reset pod->error_count to
* zero, then call add_error_message multiple times, once with each message. */
++Pod->active_status->error_count;
return;
}
/***********************************************************************/
int update_status(const int cur_status)
{
/* REQUIRES: parallel port open for reading
* cur_status is what the printer status SHOULD be,
* barring other errors....
* MODIFIES: active status file, stderr
* EFFECTS: reads parallel port status and reports it into
* printer active status file.
* ASSUMES: We will not report more than PD_ERROR_MAX errors.
* RETURNS: TRUE if reading status was successful, else FALSE.
*/
char options[PD_STR_MAX]; /* What options we've detected */
/* We hold signals here so we do not leave POD data base in a
* corrupted state in PDLocalWriteStatus
*/
hold_signals();
Pod->active_status->operational_status = cur_status;
Pod->active_status->error_count = 0;
/* If warnings info is allowed, add the optional driver version now. */
if (PrintWarnings) {
(void) add_error_message(PD_MSG_MASK_VERSION, LaserjetPJLDriverVersion);
}
/* Set the page size to whatever the user has requested. */
Pod->active_status->media_size = UserPageSize;
/* Set the page size validation mask. We use the different
* masks to support different iresolutions
*/
switch(VertRes) {
case 600:
if (FullBleed) {
Pod->active_status->validation_mask = PAGEMASK_BLEED_600;
} else {
Pod->active_status->validation_mask = PAGEMASK_600;
}
break;
default:
case 300:
if (FullBleed) {
Pod->active_status->validation_mask = PAGEMASK_BLEED_300;
} else {
Pod->active_status->validation_mask = PAGEMASK_300;
}
break;
}
if (DEBUG >= MEDIUMDETAIL) {
fprintf(stderr, "%s: Setting validation mask to %d (%d dpi).\n",
ProgramName, Pod->active_status->validation_mask,
VertRes);
}
/* Now handle reporting the options field: First null out the
* options field to allow filling it in using strncat
*/
options[0] = '\0';
/* Now report the resolution option selected: NOTE that the PS
* interpreter uses this information to override the POD .config
* file resolution, so that printers can switch resolution on the
* fly. If the interpreter finds this option string in any POD
* .status file, it uses this resolution for its calculations and
* interpretation.
*/
(void) sprintf(options, "CurrentRes=%dx%d ", HorizRes, VertRes);
/* NOTE TO DEVELOPER:
* Add your own options messages here, if necessary,
* by carefully strncat'ing additional strings onto options
* before we write them to the Pod->active_status structure, below
*/
/* Done setting our options string, copy it into Pod->active_status */
(void) strncpy(Pod->active_status->printer_options, options, PD_STR_MAX-1);
/* Now NULL terminate the string just in case */
Pod->active_status->printer_options[PD_STR_MAX-1] = NULL;
if (DEBUG) {
fprintf(stderr,"%s: Updating printer POD status file.\n", ProgramName);
}
PDLocalWriteStatus(PrinterName, Pod->active_status, ErrMsgs);
release_signals();
return TRUE;
}
/***********************************************************************/
void send_stiff_file(void)
{
unsigned int imagenumber = 0;
if (DEBUG) {
fprintf(stderr, "%s: Processing input STIFF file.\n", ProgramName);
}
/* Open the image file for reading */
ImageStream = STOpen(fileno(stdin), ST_READ);
if (!ImageStream) {
fprintf(stderr,"%s: ERROR: No data read from input stream!\n",
ProgramName);
STPerror(ProgramName);
cleanup_and_quit(BAD_DATA_SHORT_FILE);
}
/* Printer is successfully set up, let's start sending images to it. */
imagenumber = 0;
while (!feof(infile)) {
imagenumber++; /* keep track of which page we're sending */
if (!read_image_header(ImageStream, &ImageHeader)) {
if (imagenumber == 1) {
fprintf(stderr,"%s: ERROR: Failure while reading image header.\n", ProgramName);
cleanup_and_quit(BAD_DATA_SHORT_FILE);
} else {
break; /* OK to run out of data after first page. */
}
}
if (DEBUG) {
fprintf(stderr, "%s: Read header of image #%d.\n", ProgramName,
imagenumber);
print_image_info(&ImageHeader);
}
if (!is_valid_format(&ImageHeader)) {
fprintf(stderr, "%s: ERROR: Image not in STIFF packed 1-bit K "
"format.\n", ProgramName);
fprintf(stderr, "\tOnly STIFF packed 1-bit K format is "
" supported.\n\n");
cleanup_and_quit(BAD_DATA_FORMAT);
}
if (imagenumber == 1) {
/* initialize_printer is the first rotuine that sends output to
* the printer. We wait until we read the stiff file in case
* it takes a long time for the file to be RIP'ed. This is
* important with network printers because we don't want to
* make a network connection to a printer until we have image data
* to send. If we did, we would tie up the printer while we RIP
* or the printer might time out if we do not send data to it
* for some period of time (causing the connection to be broken
* by the printer).
*/
if (!initialize_printer(stdout)) {
fprintf(stderr,"%s: ERROR: Failed to initialize printer.\n",
ProgramName);
cleanup_and_quit(BAD_TRANSMISSION);
}
}
if (!send_image(stdout, &ImageHeader, InBuf)) {
fprintf(stderr, "%s: ERROR while sending page %d.\n",
ProgramName, imagenumber);
}
if (DEBUG) {
fprintf(stderr, "%s: Finished sending image #%d.\n",
ProgramName, imagenumber);
}
} /* While */
}
/***********************************************************************/
void send_pcl_file(void)
{
int inbytesleft;
char* ptr;
boolean empty_infile = FALSE;
boolean found_reset = FALSE;
/* Send a file already in PCL mode (used with fastpath by model file) */
if (DEBUG) {
fprintf(stderr, "%s: Processing input PCL file.\n", ProgramName);
}
if (!initialize_printer(stdout)) {
fprintf(stderr,"%s: ERROR: Failed to initialize printer.\n",ProgramName);
cleanup_and_quit(BAD_TRANSMISSION);
}
while (!feof(infile)) {
inbytesleft = fread(InBuf,1,InBufSize,infile);
if (inbytesleft > 0) empty_infile = FALSE;
if (ferror(infile)) {
fprintf(stderr, "%s: Error #%d reading input pipe, exiting.\n",
ProgramName, errno);
cleanup_and_quit(BAD_DATA_SHORT_FILE);
}
if (!found_reset) {
/* We have to strip the reset from the input path so it will
* not reset the pcl commands we just prepended to the file
*/
if (ptr = strstr(InBuf,"\033E")) {
*ptr=' ';
*(ptr+1)=' ';
found_reset=TRUE;
}
}
(void) fwrite(InBuf,1,inbytesleft,stdout);
if (ferror(stdout)) {
int exitcode = errno;
fprintf(stderr, "%s: Error #%d writing output pipe.\n",
ProgramName, exitcode);
cleanup_and_quit(BAD_TRANSMISSION);
}
}
/* An empty input file is a downstream error, detect and exit with
* an error status.
*/
if (empty_infile == TRUE) {
fprintf(stderr, "%s Error: Input file empty\n",ProgramName);
cleanup_and_quit(BAD_DATA_SHORT_FILE);
}
fflush(stdout);
}
/***********************************************************************/
int main(int argc, char* argv[])
{
SLPrinterStruct *pinfo; /* Printer info structure */
/* First of all, set the global program name from argv[0]: */
(void) set_program_name(argv[0]);
/* Catch the killing signals so that proper cleanup can be done */
(void) setup_signal_handler();
/* Now handle all standard input flags according to SGI specifications */
if (!process_args(argc, argv)) return(BAD_ARG);
/* Set the model type based on the formal name of the printer.
* Formal name comes from the model script file. PJL and PCL
* libraries need to know the model in use.
*/
if ((SLGetPrinterInfo(PrinterName, &pinfo) == 0)) {
if (DEBUG) {
fprintf(stderr,
"%s: printer model=%s\n",ProgramName,pinfo->formal_name);
}
if (pjlSetModel(pinfo->formal_name) != LJ_EXIT_OK) {
fprintf(stderr, "%s: Unknown printer model. Assuming "
"LaserJet 4Mv.\n",ProgramName);
}
}
/* Get the model for our use. If there was an error it will be
* some default value.
*/
PrinterModel = pjlGetModel();
/* Open the POD database files. */
if (PDLocalReadInfo(PrinterName, &Pod, &PodModTime) < 0) {
fprintf(stderr, "%s: Could not open required POD database "
" files for printer %s.\n", ProgramName, PrinterName);
fprintf(stderr, "%s: Are you sure all required POD files are "
"properly installed?\n", ProgramName);
PDPerror(ProgramName);
cleanup_and_quit(BAD_POD_ACCESS);
}
if (DEBUG > MEDIUMDETAIL) {
fprintf(stderr, "%s: Successfully read printer POD files.\n",
ProgramName);
}
/* If status-only is true, exit after updating status. */
if (StatusOnly == TRUE) exit(update_status(PD_STATUS_IDLE));
/* Allocate a buffer to read input */
if (InBufSize < 128) InBufSize = BUFDEFSIZE;
InBuf = (unchar*) safeMalloc(InBufSize);
if (!InBuf) {
fprintf(stderr, "%s: Unable to allocate page buffer of %d bytes\n",
ProgramName, BUFDEFSIZE);
cleanup_and_quit(BAD_MEMORY_ALLOC);
}
/* There is a command line option that indicates if the file is a
* PCL file. If it is we tack on PJL and PCL commands to implement
* command line options then we send the file. Otherwise, the input
* is a Stiff raster file and we convert it to PCL raster data and
* send the PCL raster data to the printer.
*/
if (pclFile) {
send_pcl_file();
} else {
send_stiff_file();
}
cleanup_and_quit(NO_ERROR);
/*NOTREACHED*/
return 0;
}